/* eslint-disable */
import { saveAs } from 'file-saver'
import * as XLSX from 'xlsx'

function generateArray(table) {
  var out = []
  var rows = table.querySelectorAll('tr')
  var ranges = []
  for (var R = 0; R < rows.length; ++R) {
    var outRow = []
    var row = rows[R]
    var columns = row.querySelectorAll('td')
    for (var C = 0; C < columns.length; ++C) {
      var cell = columns[C]
      var colspan = cell.getAttribute('colspan')
      var rowspan = cell.getAttribute('rowspan')
      var cellValue = cell.innerText
      if (cellValue !== '' && cellValue == +cellValue) cellValue = +cellValue

      //Skip ranges
      ranges.forEach(function (range) {
        if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
          for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null)
        }
      })

      //Handle Row Span
      if (rowspan || colspan) {
        rowspan = rowspan || 1
        colspan = colspan || 1
        ranges.push({
          s: {
            r: R,
            c: outRow.length,
          },
          e: {
            r: R + rowspan - 1,
            c: outRow.length + colspan - 1,
          },
        })
      }

      //Handle Value
      outRow.push(cellValue !== '' ? cellValue : null)

      //Handle Colspan
      if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null)
    }
    out.push(outRow)
  }
  return [out, ranges]
}

function datenum(v, date1904) {
  if (date1904) v += 1462
  var epoch = Date.parse(v)
  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
}

function sheet_from_array_of_arrays(data, opts) {
  var ws = {}
  var range = {
    s: {
      c: 10000000,
      r: 10000000,
    },
    e: {
      c: 0,
      r: 0,
    },
  }
  for (var R = 0; R != data.length; ++R) {
    for (var C = 0; C != data[R].length; ++C) {
      if (range.s.r > R) range.s.r = R
      if (range.s.c > C) range.s.c = C
      if (range.e.r < R) range.e.r = R
      if (range.e.c < C) range.e.c = C
      var cell = {
        v: data[R][C],
      }
      if (cell.v == null) continue
      var cell_ref = XLSX.utils.encode_cell({
        c: C,
        r: R,
      })

      if (typeof cell.v === 'number') cell.t = 'n'
      else if (typeof cell.v === 'boolean') cell.t = 'b'
      else if (cell.v instanceof Date) {
        cell.t = 'n'
        cell.z = XLSX.SSF._table[14]
        cell.v = datenum(cell.v)
      } else cell.t = 's'

      ws[cell_ref] = cell
    }
  }
  if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)
  return ws
}

function Workbook() {
  if (!(this instanceof Workbook)) return new Workbook()
  this.SheetNames = []
  this.Sheets = {}
}

function s2ab(s) {
  var buf = new ArrayBuffer(s.length)
  var view = new Uint8Array(buf)
  for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff
  return buf
}

/**
 * 构建excel表头
 * @param revealList 列表页面展示的表头
 * @returns {[]} excel表格展示的表头
 */
function buildHeader(revealList) {
  let excelHeader = []
  // 构建生成excel表头需要的数据结构
  getHeader(revealList, excelHeader, 0, 0)
  // 多行表头长短不一，短的向长的看齐，不够的补上行合并占位符
  let max = Math.max(...excelHeader.map(a => a.length))
  excelHeader.filter(e => e.length < max).forEach(e => pushRowSpanPlaceHolder(e, max - e.length))
  return excelHeader
}
/**
 * 生成头部
 * @param headers 展示的头部
 * @param excelHeader excel头部
 * @param deep 深度
 * @param perOffset 前置偏移量
 * @returns {number}  后置偏移量
 */
function getHeader(headers, excelHeader, deep, perOffset) {
  let offset = 0
  let cur = excelHeader[deep]
  if (!cur) {
    cur = excelHeader[deep] = []
  }
  // 填充行合并占位符
  pushRowSpanPlaceHolder(cur, perOffset - cur.length)
  for (let i = 0; i < headers.length; i++) {
    let head = headers[i]
    cur.push(head.label)
    if (head.hasOwnProperty('children') && Array.isArray(head.children) && head.children.length > 0) {
      let childOffset = getHeader(head.children, excelHeader, deep + 1, cur.length - 1)
      // 填充列合并占位符
      pushColSpanPlaceHolder(cur, childOffset - 1)
      offset += childOffset
    } else {
      offset++
    }
  }
  return offset
}
/**
 * 填充行合并占位符
 * */
function pushRowSpanPlaceHolder(arr, count) {
  for (let i = 0; i < count; i++) {
    arr.push('!$ROW_SPAN_PLACEHOLDER')
  }
}
// 填充列合并占位符
function pushColSpanPlaceHolder(arr, count) {
  for (let i = 0; i < count; i++) {
    arr.push('!$COL_SPAN_PLACEHOLDER')
  }
}

/**
 * 合并头部单元格
 **/
function doMerges(arr) {
  // 要么横向合并 要么纵向合并
  let deep = arr.length
  let merges = []
  // 再处理纵向合并
  let colLength = arr[0].length
  for (let y = 0; y < deep; y++) {
    // 先处理横向合并
    let row = arr[y]
    let colSpan = 0
    for (let x = 0; x < row.length; x++) {
      if (row[x] === '!$COL_SPAN_PLACEHOLDER') {
        row[x] = undefined
        if (x + 1 === row.length) {
          merges.push({
            s: {
              r: y,
              c: x - colSpan - 1,
            },
            e: {
              r: y,
              c: x,
            },
          })
        }
        colSpan++
      } else if (colSpan > 0 && x > colSpan) {
        merges.push({
          s: {
            r: y,
            c: x - colSpan - 1,
          },
          e: {
            r: y,
            c: x - 1,
          },
        })
        colSpan = 0
      } else {
        colSpan = 0
      }
    }
  }
  for (let x = 0; x < colLength; x++) {
    let rowSpan = 0
    for (let y = 0; y < deep; y++) {
      if (arr[y][x] === '!$ROW_SPAN_PLACEHOLDER') {
        arr[y][x] = undefined
        if (y + 1 === deep) {
          merges.push({
            s: {
              r: y - rowSpan,
              c: x,
            },
            e: {
              r: y,
              c: x,
            },
          })
        }
        rowSpan++
      } else if (rowSpan > 0 && y > rowSpan) {
        merges.push({
          s: {
            r: y - rowSpan - 1,
            c: x,
          },
          e: {
            r: y - 1,
            c: x,
          },
        })
        rowSpan = 0
      } else {
        rowSpan = 0
      }
    }
  }
  return merges
}

/**
 * 根据选中的数据和展示的列，生成结果
 * @param selectionData
 * @param revealList
 */
function extractData(selectionData, revealList) {
  // 列
  let headerList = flat(revealList)
  // 导出的结果集
  let excelRows = []
  // 如果有child集合的话会用到
  let dataKeys = new Set(Object.keys(selectionData[0]))
  selectionData.some(e => {
    if (e.children && e.children.length > 0) {
      let childKeys = Object.keys(e.children[0])
      for (let i = 0; i < childKeys.length; i++) {
        dataKeys.delete(childKeys[i])
      }
      return true
    }
  })
  flatData(selectionData, list => {
    excelRows.push(...buildExcelRow(dataKeys, headerList, list))
  })
  return excelRows
}

function buildExcelRow(mainKeys, headers, rawDataList) {
  // 合计行
  let sumCols = []
  // 数据行
  let rows = []
  for (let i = 0; i < rawDataList.length; i++) {
    let cols = []
    let rawData = rawDataList[i]
    // 提取数据
    for (let j = 0; j < headers.length; j++) {
      let header = headers[j]
      // 父元素键需要行合并
      if (rawData['rowSpan'] === 0 && mainKeys.has(header.prop)) {
        cols.push('!$ROW_SPAN_PLACEHOLDER')
      } else {
        let value
        if (typeof header.exeFun === 'function') {
          value = header.exeFun(rawData)
        } else {
          value = rawData[header.prop]
        }
        cols.push(value)
        // 如果该列需要合计,并且是数字类型
        if (header['summable'] && typeof value === 'number') {
          sumCols[j] = (sumCols[j] ? sumCols[j] : 0) + value
        }
      }
    }
    rows.push(cols)
  }
  // 如果有合计行
  if (sumCols.length > 0) {
    rows.push(...sumRowHandle(sumCols))
  }
  return rows
}

function sumRowHandle(sumCols) {
  //TODO
  return []
}
/**
 * 展开数据，为了实现父子关系的数据进行行合并
 * @param list
 * @param eachDataCallBack
 */
function flatData(list, eachDataCallBack) {
  let resultList = []
  for (let i = 0; i < list.length; i++) {
    let data = list[i]
    let rawDataList = []
    // 每个子元素都和父元素合并成一条数据
    if (data.children && data.children.length > 0) {
      for (let j = 0; j < data.children.length; j++) {
        delete data.children[j].bsm
        let copy = Object.assign({}, data, data.children[j])
        rawDataList.push(copy)
        copy['rowSpan'] = j > 0 ? 0 : data.children.length
      }
    } else {
      data['rowSpan'] = 1
      rawDataList.push(data)
    }
    resultList.push(...rawDataList)
    if (typeof eachDataCallBack === 'function') {
      eachDataCallBack(rawDataList)
    }
  }
  return resultList
}
// 扁平头部
function flat(revealList) {
  let result = []
  revealList.forEach(e => {
    if (e.hasOwnProperty('children') && Array.isArray(e.children) && e.children.length > 0) {
      result.push(...flat(e.children))
    } else if (e.hasOwnProperty('exeFun')) {
      result.push(e)
    } else if (e.hasOwnProperty('prop')) {
      result.push(e)
    }
  })
  return result
}
// 设置自适应宽度
function autoWidthfun(data, ws) {
  /*设置worksheet每列的最大宽度*/
  const colWidth = data.map(row =>
    row.map(val => {
      /*先判断是否为null/undefined*/
      if (val == null) {
        return {
          wch: 10,
        }
      } else if (val.toString().charCodeAt(0) > 255) {
        /*再判断是否为中文*/
        return {
          wch: val.toString().length * 2,
        }
      } else {
        return {
          wch: val.toString().length,
        }
      }
    })
  )
  /*以第一行为初始值*/
  let result = colWidth[0]
  for (let i = 1; i < colWidth.length; i++) {
    for (let j = 0; j < colWidth[i].length; j++) {
      if (result[j]['wch'] < colWidth[i][j]['wch']) {
        result[j]['wch'] = colWidth[i][j]['wch']
      }
    }
  }
  return (ws['!cols'] = result)
}
export function export_table_to_excel(id) {
  var theTable = document.getElementById(id)
  var oo = generateArray(theTable)
  var ranges = oo[1]

  /* original data */
  var data = oo[0]
  var ws_name = 'SheetJS'

  var wb = new Workbook(),
    ws = sheet_from_array_of_arrays(data)

  /* add ranges to worksheet */
  // ws['!cols'] = ['apple', 'banan'];
  ws['!merges'] = ranges

  /* add worksheet to workbook */
  wb.SheetNames.push(ws_name)
  wb.Sheets[ws_name] = ws

  var wbout = XLSX.write(wb, {
    bookType: 'xlsx',
    bookSST: false,
    type: 'binary',
  })

  saveAs(
    new Blob([s2ab(wbout)], {
      type: 'application/octet-stream',
    }),
    'test.xlsx'
  )
}

// 使用简单数据导出
export function export_json_to_excel({
  dynamicMulti = [],
  multiHeader = [],
  header,
  data,
  fileName,
  wsName,
  merges = [],
  autoWidth = true,
  bookType = 'xlsx',
} = {}) {
  /* original data */
  fileName = fileName || 'excel-list'
  wsName = wsName || 'Sheet1'
  data = [...data]

  if (dynamicMulti.length > 0) {
    // 如果是多级动态数据
    let excelHeader = buildHeader(dynamicMulti)
    for (let i = dynamicMulti.length - 1; i > -1; i--) {
      if (excelHeader[i] !== undefined) {
        data.unshift(excelHeader[i])
      }
    }
    merges = doMerges(data)
    var wb = new Workbook(),
      ws = sheet_from_array_of_arrays(data)
    ws['!merges'] = merges
  } else {
    data.unshift(header)
    for (let i = multiHeader.length - 1; i > -1; i--) {
      data.unshift(multiHeader[i])
    }
    var wb = new Workbook(),
      ws = sheet_from_array_of_arrays(data)
    if (merges.length > 0) {
      if (!ws['!merges']) ws['!merges'] = []
      // 单元格合并S
      merges.forEach(item => {
        ws['!merges'].push(XLSX.utils.decode_range(item))
      })
    }
  }
  if (autoWidth) {
    /*设置worksheet每列的最大宽度*/
    const colWidth = data.map(row =>
      row.map(val => {
        /*先判断是否为null/undefined*/
        if (val == null) {
          return {
            wch: 10,
          }
        } else if (val.toString().charCodeAt(0) > 255) {
          /*再判断是否为中文*/
          return {
            wch: val.toString().length * 2,
          }
        } else {
          return {
            wch: val.toString().length,
          }
        }
      })
    )
    /*以第一行为初始值*/
    let result = colWidth[0]
    for (let i = 1; i < colWidth.length; i++) {
      for (let j = 0; j < colWidth[i].length; j++) {
        if (result[j]['wch'] < colWidth[i][j]['wch']) {
          result[j]['wch'] = colWidth[i][j]['wch']
        }
      }
    }
    ws['!cols'] = result
  }

  /* add worksheet to workbook */
  wb.SheetNames.push(wsname)
  wb.Sheets[wsname] = ws

  var wbout = XLSX.write(wb, {
    bookType: bookType,
    bookSST: false,
    type: 'binary',
  })
  saveAs(
    new Blob([s2ab(wbout)], {
      type: 'application/octet-stream',
    }),
    `${fileName}.${bookType}`
  )
}

// 导出多级动态表格
export function export_json_to_dynamicexcel({
  dynamicMulti = [],
  dynamicData = [],
  fileName,
  wsName,
  autoWidth = true,
  bookType = 'xlsx',
} = {}) {
  /* original data */
  fileName = fileName || 'excel-list'
  wsName = wsName || 'Sheet1'
  // 如果是多级动态数据
  let excelHeader = buildHeader(dynamicMulti)
  // 提取数据
  let dataList = extractData(dynamicData, dynamicMulti)
  excelHeader.push(...dataList)

  let merges = doMerges(excelHeader)

  var wb = new Workbook(),
    ws = sheet_from_array_of_arrays(excelHeader)
  ws['!merges'] = merges
  if (autoWidth) {
    autoWidthfun(excelHeader, ws)
  }
  /* add worksheet to workbook */
  wb.SheetNames.push(wsName)
  wb.Sheets[wsName] = ws

  var wbout = XLSX.write(wb, {
    bookType: bookType,
    bookSST: false,
    type: 'binary',
  })
  saveAs(
    new Blob([s2ab(wbout)], {
      type: 'application/octet-stream',
    }),
    `${fileName}.${bookType}`
  )
}
